﻿#region License
/* Copyright (C) 2009 Tim Coulter
 * 
 * This file is part of ExtremeML.
 * 
 * ExtremeML is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation either version
 * 2 of the License, or (at your option) any later version.
 * 
 * ExtremeML is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
*/
#endregion

using System;
using System.Collections.Generic;
using System.Reflection;
using DocumentFormat.OpenXml.Packaging;

namespace ExtremeML.Builder
{
	/// <summary>
	/// Exposes the design parameters for a generated concrete implementation of PartWrapper.
	/// </summary>
	public class PartWrapperModel : WrapperModelBase
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="PartWrapperModel"/> class.
		/// </summary>
		/// <param name="target">A <see cref="WrapperTypeInfo"/> that specifies the form of the generated class.</param>
		/// <param name="isCollectionMember">Indicates whether instances of this class form a collection.</param>
		public PartWrapperModel(
			WrapperTypeInfo target,
			bool isCollectionMember)
			: base(target.WrappedType, target)
		{
			IsCollectionMember = isCollectionMember;
		}

		/// <summary>
		/// Gets a value that indicates whether instances of this class form a collection.
		/// </summary>
		public bool IsCollectionMember { get; private set; }

		/// <summary>
		/// Returns the set of <see cref="WrapperPropertyMapping"/> for this wrapper type.
		/// </summary>
		protected override IEnumerable<WrapperPropertyMapping> GetProperties()
		{
			foreach (var info in GetQualifyingProperties())
			{
				yield return CreatePropertyMapping(info);
			}
		}

		/// <summary>
		/// Returns the dictionary of custom patterns for this wrapper type.
		/// </summary>
		protected override Dictionary<string, CustomPattern> GetCustomPatterns()
		{
			var result = base.GetCustomPatterns();

			AddCreateDefaultPartPattern(result);
			AddCollectionPartPattern(result);

			return result;
		}

		/// <summary>
		/// Returns public PropertyInfo for the passed type and its ancestor chain,
		/// ignoring ancestor types that do not expose domain-specific properties.
		/// </summary>
		private PropertyInfo[] GetQualifyingProperties()
		{
			var type = WrappedType;

			var exclude = new List<Type>
			{
				null,
				typeof(object),
				typeof(OpenXmlPart)
			};

			var result = new List<PropertyInfo>();

			while (!exclude.Contains(type))
			{
				result.AddRange(type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly));
				type = type.BaseType;
			}

			return result.ToArray();
		}

		/// <summary>
		/// Returns whether properties of the passed <see cref="Type"/> should be ignored.
		/// </summary>
		protected override bool IsIgnoredPropertyType(
			Type type)
		{
			return type.IsSubclassOf(typeof(OpenXmlPackage));
		}


		/// <summary>
		/// Adds the relevant CreatePartPattern to the passed patterns dictionary.
		/// </summary>
		private void AddCreateDefaultPartPattern(
			Dictionary<string, CustomPattern> paterns)
		{
			if (typeof(IFixedContentTypePart).IsAssignableFrom(WrappedType))
			{
				var pattern = new CustomPattern("FixedContentTypePartPattern");
				paterns.Add(pattern.Name, pattern);
			}
			else
			{
				var pattern = new CustomPattern("VariableContentTypePartPattern");
				paterns.Add(pattern.Name, pattern);
			}
		}

		/// <summary>
		/// Conditionally adds the CollectionPartPattern to the passed patterns dictionary.
		/// </summary>
		private void AddCollectionPartPattern(
			Dictionary<string, CustomPattern> paterns)
		{
			if (IsCollectionMember)
			{
				var pattern = new CustomPattern("CollectionPartPattern");
				paterns.Add(pattern.Name, pattern);
			}
		}
	}
}
